// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CommandLine.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.CommandLine
{
    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Reflection;

    using Org.OpenTrader.Framework.CommandGroups;
    using Org.OpenTrader.Framework.Entities;

    #endregion

    /// <summary>
    /// The command line.
    /// </summary>
    public class CommandLine
    {
        #region Constants and Fields

        /// <summary>
        /// The Identity.
        /// </summary>
        private static readonly Identity Identity = Identity.Create(System.Reflection.MethodBase.GetCurrentMethod());

        #endregion

        #region Properties

        /// <summary>
        /// Gets ProcessName.
        /// </summary>
        private static string ProcessName
        {
            get
            {
                return System.Diagnostics.Process.GetCurrentProcess().ProcessName;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The process.
        /// </summary>
        /// <param name="commandLine">
        /// The command line.
        /// </param>
        public static void Process(string[] commandLine)
        {
            // Interactive mode
            if (commandLine.Length == 0)
            {
                InteractiveMode();
            }
            else if (commandLine[0].Equals("help"))
            {
                UsageLong();
            }
            else if (commandLine.Length >= 2)
            {
                // The command group
                var commandGroup = commandLine[0];

                // The command inside the command group
                var commandName = commandLine[1];

                // The given parameters (if any)
                var commandArgumentsCount = Math.Max(0, commandLine.Length - 2);
                var commandArguments = new string[] { };
                if (commandArgumentsCount > 0)
                {
                    commandArguments = new string[commandArgumentsCount];
                    Array.Copy(commandLine, 2, commandArguments, 0, commandArgumentsCount);
                }

                var executing = string.Format(
                    "{0} CommandGroup={1} Command={2} CommandArguments={3}", 
                    ProcessName.ToUpper(), 
                    commandGroup, 
                    commandName, 
                    "{{" + string.Join(",", commandArguments) + "}}");
                Console.WriteLine(executing);

                // Try to run the command
                var result = RunCommand(commandGroup, commandName, commandArguments);
                if (!result)
                {
                    Console.WriteLine(string.Format("FAILURE - {0}", result.DetailedMessage));
                }
                else
                {
                    Console.WriteLine("SUCCESS");
                }
            }
            else
            {
                UsageShort();
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// The console cancel key press.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private static void ConsoleCancelKeyPress(object sender, ConsoleCancelEventArgs e)
        {
            // Message
            Console.WriteLine("Exiting");
        }

        /// <summary>
        /// The display group.
        /// </summary>
        /// <param name="title">
        /// The title.
        /// </param>
        /// <param name="commandGroups">
        /// The command groups.
        /// </param>
        private static void DisplayGroup(string title, IDictionary<string, CommandGroup> commandGroups)
        {
            if (commandGroups.Count == 0)
            {
                return;
            }

            Console.WriteLine();
            Console.WriteLine("** " + title);
            Console.WriteLine();

            foreach (var keyValuePair in commandGroups)
            {
                var commandGroupName = keyValuePair.Key;
                var commandGroup = keyValuePair.Value;

                Console.WriteLine(string.Format("{0,-20}{1}", commandGroupName, commandGroup.Help));
                Console.WriteLine();
                foreach (var command in commandGroup.Commands)
                {
                    Console.WriteLine(string.Format("  {0,-18}{1}", command.Key, command.Value.Help));
                    Console.WriteLine(string.Format("  {0,-18}{1}{2} {3}", string.Empty, "Usage: ", ProcessName, command.Value.Usage));
                    Console.WriteLine();
                }
            }
        }

        /// <summary>
        /// The bad parameters exception.
        /// </summary>
        private static void InteractiveMode()
        {
            // ctrl-c handler
            Console.CancelKeyPress += ConsoleCancelKeyPress;

            do
            {
                Console.Write("otctl> ");
                var input = Console.ReadLine();
                if (input == null)
                {
                    return; // ctrl-c
                }

                var commandLine = input.Split(new[] { ' ' });

                try
                {
                    Process(commandLine);
                }
                catch
                {
                }
            }
            while (true);
        }

        /// <summary>
        /// The run command.
        /// </summary>
        /// <param name="commandGroupName">
        /// The command group name.
        /// </param>
        /// <param name="commandName">
        /// The command name.
        /// </param>
        /// <param name="commandArguments">
        /// The command arguments.
        /// </param>
        /// <returns>
        /// </returns>
        private static Status RunCommand(string commandGroupName, string commandName, string[] commandArguments)
        {
            // All the commands
            IDictionary<string, CommandGroup> allCommandGroups = new Dictionary<string, CommandGroup>();

            // Add Builtin
            foreach (var commandGroup in Builtins.CommandGroups)
            {
                allCommandGroups.Add(commandGroup);
            }

            // Add Extensions
            foreach (var commandGroup in Extensions.CommandGroups)
            {
                allCommandGroups.Add(commandGroup);
            }

            // Run
            return RunCommand(allCommandGroups, commandGroupName, commandName, commandArguments);
        }

        /// <summary>
        /// The run command.
        /// </summary>
        /// <param name="commandGroups">
        /// The command groups.
        /// </param>
        /// <param name="commandGroupName">
        /// The command group name.
        /// </param>
        /// <param name="commandName">
        /// The command name.
        /// </param>
        /// <param name="commandArguments">
        /// The command arguments.
        /// </param>
        /// <returns>
        /// </returns>
        private static Status RunCommand(IDictionary<string, CommandGroup> commandGroups, string commandGroupName, string commandName, string[] commandArguments)
        {
            if (!commandGroups.ContainsKey(commandGroupName))
            {
                return Status.Failure(Identity, string.Format("Command Group {0} wasn't found", commandGroupName));
            }

            var group = commandGroups[commandGroupName];

            if (!group.Commands.ContainsKey(commandName))
            {
                return Status.Failure(Identity, string.Format("Command Group {0} doesn't contain command {1}", commandGroupName, commandName));
            }

            var command = group.Commands[commandName];

            try
            {
                return command.Execute(commandArguments);
            }
            catch (Exception ex)
            {
                return Status.Failure(
                    Identity, string.Format("RunCommand: Got exception while calling Execute on command {0}/{1}\n\n{2}", commandGroupName, commandName, ex));
            }
        }

        /// <summary>
        /// Footer of usage message
        /// </summary>
        private static void UsageFooter()
        {
        }

        /// <summary>
        /// Header of usage message
        /// </summary>
        private static void UsageHeader()
        {
            // Console.WriteLine(string.Format("{0}, Open Trader Control Application", ProcessName));
        }

        /// <summary>
        /// Detailed usage message
        /// </summary>
        private static void UsageLong()
        {
            UsageHeader();

            Console.WriteLine(string.Empty);
            Console.WriteLine("** CURRENT CONFIGURATION");
            Console.WriteLine(string.Empty);
            Console.WriteLine("ConnectionString");
            Console.WriteLine("  {0}", Singleton<DB>.Instance.SQLConnectionString.Replace(";", "\n  "));
            Console.WriteLine(string.Empty);

            DisplayGroup("BUILTINS COMMAND GROUPS", Builtins.CommandGroups);

            DisplayGroup("EXTENSIONS COMMAND GROUPS", Extensions.CommandGroups);

            UsageFooter();
        }

        /// <summary>
        /// Short usage message
        /// </summary>
        private static void UsageShort()
        {
            UsageHeader();

            Console.WriteLine(string.Empty);
            Console.WriteLine("Use otctl help for long help");

            UsageFooter();
        }

        #endregion
    }
}